package tetris;

import java.awt.*;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by IntelliJ IDEA.
 * User: AShmygin
 * Date: 10/19/11
 * Time: 6:20 PM
 * To change this template use File | Settings | File Templates.
 */

public class CharBoard implements Board<String> {
    private static final int MIN_ROWS_NUMBER = 2;
    private static final int MAX_ROWS_NUMBER = 20;
    private static final int MIN_COLS_NUMBER = 2;
    private static final int MAX_COLS_NUMBER = 10;

    private List<Brick> bricks = new ArrayList<Brick>();
    private List<Brick> fallingBricks = new ArrayList<Brick>();
    private int columnsNumber;
    private int rowsNumber;
    private boolean gameOver;


    public CharBoard(int columnsNumber, int rowsNumber) {

        if (columnsNumber > MAX_COLS_NUMBER || columnsNumber < MIN_COLS_NUMBER) {
            throw new IllegalArgumentException("Illegal columns number");
        }
        if (rowsNumber > MAX_ROWS_NUMBER || rowsNumber < MIN_ROWS_NUMBER) {
            throw new IllegalArgumentException("Illegal rows number");
        }

        this.columnsNumber = columnsNumber;
        this.rowsNumber = rowsNumber;
    }

    public String render() {
        char[][] cup = new char[rowsNumber][columnsNumber];
        for (int i = 0; i < rowsNumber; i++) {
            for (int j = 0; j < columnsNumber; j++) {
                cup[i][j] = '*';
            }
        }
        for (Brick brk : fallingBricks) {
            cup[brk.getRow()][brk.getCol()] = brk.getSymbol();
        }
        for (Brick brk : bricks) {
            cup[brk.getRow()][brk.getCol()] = brk.getSymbol();
        }
        StringBuilder sb = new StringBuilder();
        for (int i = rowsNumber - 1; i >= 0; i--) {
            for (int j = 0; j < columnsNumber; j++) {
                sb.append(cup[i][j]);
            }
            sb.append('\n');
        }
        return sb.toString();
    }

    public void drop(Brick brick) {
        if (isFallingBrick()) {
            throw new IllegalStateException("The brick is falling right now.");
        }
        Point p = getDropPosition();
        brick.setCol(p.x);
        brick.setRow(p.y);
        fallingBricks.add(brick);

        //To change body of implemented methods use File | Settings | File Templates.
    }

    private Point getDropPosition() {
        return new Point((columnsNumber - 1) / 2, rowsNumber - 1);
    }

    public void tick() {
        if (isGameOver()) {
            throw new IllegalStateException();
        }
        if (fallingBricks.isEmpty()) {
            if (canDrop()) {
                drop(new CharBrick('O'));
            } else {
                setGameOver(true);
            }
            return;
        }
        List<Brick> landedBricks = new ArrayList<Brick>();
        for (Brick brick : fallingBricks) {
            if (checkSpaceAvailable(brick)) {
                int row = brick.getRow() - 1;
                brick.setRow(row);
            } else {
                landedBricks.add(brick);
            }
        }
        for (Brick brick : landedBricks) {
            fallingBricks.remove(brick);
            bricks.add(brick);
        }
    }

    private boolean canDrop() {
        Point p = getDropPosition();
        for (Brick brick : bricks) {
            if (brick.getCol() == p.x && brick.getRow() == p.y) {
                return false;
            }
        }
        return true;
    }

    private boolean checkSpaceAvailable(Brick brick) {
        boolean canFall = true;
        canFall = canFall && checkNotHitsTheBottom(brick);
        canFall = canFall && checkNotHitsAnotherBlock(brick);
        return canFall;
    }

    private boolean checkNotHitsAnotherBlock(Brick brick) {
        return !brickExists(brick.getCol(), brick.getRow() - 1);
    }

    private boolean checkNotHitsTheBottom(Brick brick) {
        return brick.getRow() != 0;
    }

    public boolean isFallingBrick() {
        return !fallingBricks.isEmpty();
    }

    public Boolean brickExists(int column, int row) {

        for (Brick brick : fallingBricks) {
            if (brick.getCol() == column && brick.getRow() == row) {
                return true;
            }
        }
        for (Brick brick : bricks) {
            if (brick.getCol() == column && brick.getRow() == row) {
                return true;
            }
        }
        return false;
    }

    public Integer getColumns() {
        return columnsNumber;
    }

    public Integer getRows() {
        return rowsNumber;
    }

    public Boolean isGameOver() {
        return gameOver;
    }

    public void setGameOver(boolean gameOver) {
        this.gameOver = gameOver;
    }
}
